package jp.ac.nitech.matlab.najm.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import jp.ac.nitech.matlab.najm.io.LiveTrace;
import jp.ac.nitech.matlab.najm.io.TraceFactory;
import jp.ac.nitech.matlab.najm.net.Packet;
import jp.ac.nitech.matlab.najm.net.Protocol;


/**
 * <[  بسم الله الرحمان الرحيم  ]
 * @author k-hon
 * Client Protocol:
 * 		
 * Rules:
 * 	
 */

public class Client extends Thread{
	//private static Client instance = new Client();

	/*public static Client getInstance(){
		return instance;
	}*/
	private static final int BUFFER_SIZE = 131072; //tmp
	private Socket ctrlsocket;
	private Socket datasocket;
	private OutputStream os ;
	private InputStream is;
	private Packet p;
	private int ID;
	private int dataport;
	private String cmd, code, target;
	String root;
	LiveTrace trace = (LiveTrace)TraceFactory.getTrace();

	public Client(int id, String cmd, String code, String target){
		super();
		this.ID = id; this.cmd = cmd; this.code = code; this.target = target;
		try{
			trace.info(this, ID, "Starting client");
			ctrlsocket = new Socket(CSetting.getInstance().getSetting("master.addr"), 
					Integer.parseInt(CSetting.getInstance().getSetting("master.port")));
			dataport = Integer.parseInt(CSetting.getInstance().getSetting("snode.port"));
			root = CSetting.getInstance().getSetting("docRoot");
			is = ctrlsocket.getInputStream();
			os = ctrlsocket.getOutputStream();
			}catch(Exception e){
				e.printStackTrace();
		}
	}
	
	
	public void run(){
		try{
			//send control packet to master
			trace.info(this, ID, "Packet.CTRL ==> master : ["+cmd+code+target+"]");
			p = new Packet(Packet.CTRL);
			p.setControl(cmd, code, target);
			os.write(p.buffer); os.flush();
			//wait response (list of SNodes)
			p = new Packet(Packet.CTRL);
			is.read(p.buffer);
			trace.info(this, ID, " ["+p.getCtrlCmd()+p.getCtrlTarget()+"] from "+ctrlsocket.getRemoteSocketAddress());
			// parse response from server ==> SNodes list
			   String[] snodes = {"127.0.0.1"};
			   datasocket = new Socket(snodes[0], dataport);
				p = new Packet(Packet.CTRL);
				p.setControl(cmd, code, target);
				datasocket.getOutputStream().write(p.buffer); os.flush();
			//  assume done
			if(cmd.compareTo(Protocol.REQ_GET)==0) 
				getFile(datasocket, target);
			else if(cmd.compareTo(Protocol.REQ_PUT)==0) 
				putFile(datasocket, target);
			else { //will never come here :) cmd is set for sure
				this.fin(os);
				}
		ctrlsocket.close();
		datasocket.close();
		}catch(Exception e){
			e.printStackTrace();
		}
		//net the file (send or rcv)
		    //send
			//sendFile(target);
			//rcv
	}
	
	private void getFile(Socket sock, String file){
		try{
			InputStream nis = sock.getInputStream();
			//String root = Setting.getInstance().getSetting("rootDir");
			byte[] buffer = new byte[BUFFER_SIZE];
			trace.info(this, ID, " server <=====data=| "+sock.getRemoteSocketAddress());
			int i = 0; int j=0;
			long t1 = System.currentTimeMillis();
			while((i=nis.read())>-1){
				buffer[j] = (byte)i;
				//System.out.print(buffer[j]);
				j++;
			}
			//System.out.println();
			long t2 = System.currentTimeMillis();
			double rate = 1000*j/(t2-t1);
			trace.info(this, ID, " transfer time: "+(t2-t1)+" for "+j+" bytes, hence "+rate+" B/s");
			trace.info(this, ID, " server <=====data=| ");
			trace.info(this, ID, " |=write===> "+root+target);
			FileOutputStream fos = new FileOutputStream(new File(root + file));
			fos.write(buffer, 0, j);
			fos.close();
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	private void putFile(Socket sock, String file){
		int i;
		try {
			OutputStream nos = sock.getOutputStream();
			FileInputStream fis = new FileInputStream(root+file);
			byte[] buffer = new byte[BUFFER_SIZE];
			i = fis.read(buffer);		
			trace.info(this, ID, " begin send data to "+sock.getRemoteSocketAddress());
			for (int j = 0; j < i; j++) {
				nos.write(buffer[j]);
				System.out.print(buffer[j]);
			}
			trace.info(this, ID, " end send data");
			nos.flush();
			nos.close();
		} catch (Exception e1) {
			i = -1;
			e1.printStackTrace();
		}	
	}
	
	//sends CTRL.FIN packet to the designated OutputStream
	private void fin(OutputStream os)throws IOException{
		p = new Packet(Packet.CTRL);
		p.setControl(Protocol.REQ_FIN, "", "");
		os.write(p.buffer);
		os.flush();
		os.close();
	}


}
